package com.cjj.common.utils;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.cjj.common.annotation.ExcelAnnotation;
import com.cjj.common.entity.dto.excel.SheetDTO;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @ClassName ExcelUtils
 * @Date 2021/09/17 15:30:48
 * @Created by ccc-j
 * @email ccc-ju@outlook.com
 */
public class ExcelMultipartSheetsUtils {

    /**
     * hutool--多sheet页导出
     * @param response
     * @param sheetList 每一个sheet页的数据,SheetDTO需要与clazz的可变参数的个数保持一直，即使每一页的对象都是一样的，也要传递对应，保持一致
     * @param fileName 文件名
     * @param days 需要导出的天数根据反射、动态代理操作注解，自动生成所需表头展示
     */
    public static void exportExcel(HttpServletResponse response, List<SheetDTO> sheetList, String fileName, int days){
        ExcelWriter bigWriter = ExcelUtil.getWriter(true);
        // 重命名第一个Sheet的名称，不然会默认多出一个Sheet1的页
        bigWriter.renameSheet(0, sheetList.get(0).getSheetName());
        for (SheetDTO sheet : sheetList) {
            Class clazz = sheet.getClazz();
            // 获取当前类字段
            Field[] fields = clazz.getDeclaredFields();
            List<String> fieldNames = new ArrayList<>();
            // 字段中文名称集合（获取实体中@ExcelAnnotation注解fieldName的值）
            List<String> cnNames = new ArrayList< >();
            for(Field field : fields) {
                if(!field.isAccessible()) {
                    // 关闭反射访问安全检查，为了提高速度
                    field.setAccessible(true);
                }
                String fieldName = field.getName();
                // 判断是否有@ExcelAnnotation注解
                boolean annotationPresent = field.isAnnotationPresent(ExcelAnnotation.class);
                if(annotationPresent) {
                    ExcelAnnotation annotation = field.getAnnotation(ExcelAnnotation.class);
                    if(ObjectUtil.isNotNull(Convert.toInt(annotation.fieldName())) && Integer.parseInt(annotation.fieldName()) <= days){
                        // 由于需要动态修改注解的属性，需使用JDK的动态代理 AnnotationInvocationHandler
                        InvocationHandler invocationHandler = Proxy.getInvocationHandler(annotation);
                        // 获取AnnotationInvocationHandler 的 memberValues 字段
                        try {
                            Field fieldIgnore = invocationHandler.getClass().getDeclaredField("memberValues");
                            // 因为字段是使用private final修饰，所以开启权限
                            fieldIgnore.setAccessible(true);
                            // 获取fieldIgnore
                            Map memberValues = (Map) fieldIgnore.get(invocationHandler);
                            memberValues.put("fieldIgnore", false);
                        } catch (NoSuchFieldException | IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                    if(!annotation.fieldIgnore()) {
                        fieldNames.add(fieldName);
                        String name = annotation.fieldName();
                        cnNames.add(name);
                    }else{
                        // 排除字段操作(如果为true，则不设置alias的字段将不被输出)
                        bigWriter.setOnlyAlias(true);
                    }
                }
            }
            String[] key = fieldNames.toArray(new String[0]);
            String[] reflectKey = cnNames.toArray(new String[0]);
            sheet.setProperties(key);
            sheet.setTitles(reflectKey);
            // 指定要写出的 Sheet 页
            bigWriter.setSheet(sheet.getSheetName());
            Integer[] columnWidth = sheet.getColumnWidth();
            if (columnWidth == null || columnWidth.length != sheet.getFieldAndAlias().size()) {
                // 设置默认宽度
                for (int i = 0; i < sheet.getFieldAndAlias().size(); i++) {
                    bigWriter.setColumnWidth(i, 20);
                }
            } else {
                // 设置自定义宽度
                for (int i = 0; i < columnWidth.length; i++) {
                    bigWriter.setColumnWidth(i, columnWidth[i]);
                }
            }
            // 设置字段和别名
            bigWriter.setHeaderAlias(sheet.getFieldAndAlias());
            // 设置只导出有别名的字段
            bigWriter.setOnlyAlias(true);
            // 设置默认行高
            bigWriter.setDefaultRowHeight(18);
            // 设置冻结行
            bigWriter.setFreezePane(1);
            // 一次性写出内容，使用默认样式，强制输出标题
            bigWriter.write(sheet.getCollection(), true);
            // 设置所有列为自动宽度，不考虑合并单元格
            // bigWriter.autoSizeColumnAll();
        }

        ServletOutputStream out = null;
        try {
            //response为HttpServletResponse对象
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
            response.setHeader("Content-Disposition",
                    "attachment;filename=" +
                            URLEncoder.encode(fileName+ ".xlsx", "UTF-8"));
            out = response.getOutputStream();
            bigWriter.flush(out, true);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭writer，释放内存
            bigWriter.close();
        }
        //此处记得关闭输出Servlet流
        IoUtil.close(out);
    }

    /**
     * hutool--多sheet页导出
     * @param response
     * @param sheetList 每一个sheet页的数据,SheetDTO需要与clazz的可变参数的个数保持一直，即使每一页的对象都是一样的，也要传递对应，保持一致
     * @param fileName 文件名
     */
    public static void exportExcel(HttpServletResponse response, List<SheetDTO> sheetList, String fileName){
        ExcelWriter bigWriter = ExcelUtil.getWriter(true);
        // 重命名第一个Sheet的名称，不然会默认多出一个Sheet1的页
        bigWriter.renameSheet(0, sheetList.get(0).getSheetName());
        for (SheetDTO sheet : sheetList) {
            Class clazz = sheet.getClazz();
            // 获取当前类字段
            Field[] fields = clazz.getDeclaredFields();
            List<String> fieldNames = new ArrayList<>();
            // 字段中文名称集合（获取实体中@ExcelAnnotation注解fieldName的值）
            List<String> cnNames = new ArrayList< >();
            for(Field field : fields) {
                if(!field.isAccessible()) {
                    // 关闭反射访问安全检查，为了提高速度
                    field.setAccessible(true);
                }
                String fieldName = field.getName();
                // 判断是否有@ExcelAnnotation注解
                boolean annotationPresent = field.isAnnotationPresent(ExcelAnnotation.class);
                if(annotationPresent) {
                    ExcelAnnotation annotation = field.getAnnotation(ExcelAnnotation.class);
                    if(!annotation.fieldIgnore()) {
                        fieldNames.add(fieldName);
                        String name = annotation.fieldName();
                        cnNames.add(name);
                    }else{
                        // 排除字段操作(如果为true，则不设置alias的字段将不被输出)
                        bigWriter.setOnlyAlias(true);
                    }
                }
            }
            String[] key = fieldNames.toArray(new String[0]);
            String[] reflectKey = cnNames.toArray(new String[0]);
            sheet.setProperties(key);
            sheet.setTitles(reflectKey);
            // 指定要写出的 Sheet 页
            bigWriter.setSheet(sheet.getSheetName());
            Integer[] columnWidth = sheet.getColumnWidth();
            if (columnWidth == null || columnWidth.length != sheet.getFieldAndAlias().size()) {
                // 设置默认宽度
                for (int i = 0; i < sheet.getFieldAndAlias().size(); i++) {
                    bigWriter.setColumnWidth(i, 25);
                }
            } else {
                // 设置自定义宽度
                for (int i = 0; i < columnWidth.length; i++) {
                    bigWriter.setColumnWidth(i, columnWidth[i]);
                }
            }
            // 设置字段和别名
            bigWriter.setHeaderAlias(sheet.getFieldAndAlias());
            // 设置只导出有别名的字段
            bigWriter.setOnlyAlias(true);
            // 设置默认行高
            bigWriter.setDefaultRowHeight(18);
            // 设置冻结行
            bigWriter.setFreezePane(1);
            // 一次性写出内容，使用默认样式，强制输出标题
            bigWriter.write(sheet.getCollection(), true);
            // 设置所有列为自动宽度，不考虑合并单元格
            bigWriter.autoSizeColumnAll();
        }

        ServletOutputStream out = null;
        try {
            //response为HttpServletResponse对象
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
            response.setHeader("Content-Disposition",
                    "attachment;filename=" +
                            URLEncoder.encode(new String(fileName.getBytes(StandardCharsets.UTF_8)) + DateUtil.today() + ".xlsx", "UTF-8"));
            out = response.getOutputStream();
            bigWriter.flush(out, true);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭writer，释放内存
            bigWriter.close();
        }
        //此处记得关闭输出Servlet流
        IoUtil.close(out);
    }
}
